/**
 *	The NeuroCoSA Toolkit
 *	Copyright (C) 2003-6 Stuart Meikle.
 *
 *	This is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU Lesser General Public
 *	License as published by the Free Software Foundation; either
 *	version 2.1 of the License, or (at your option) any later version.
 *
 *	This library is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *	Lesser General Public License for more details.
 *
 * @author	Stuart Meikle
 * @version	2006-halloween(mk2)
 * @license	LGPL
 */
package org.stumeikle.NeuroCoSA.PubSub;
import java.io.*;
import java.util.*;

public class Publication
{
    protected	Object		iValue;
    protected	long		iVersion;// when was it last updated
					 // where ? timestamp .... how to synchronise
    public	long		iID;	// needed to identify the item MUST
    public 	LinkedList<Subscriber>	
				iReaders;
    protected 	Object		iLocker; // who has locked the publication?

    public Publication()
    {
	construct();
    }
    
    private	void	construct()
    {
	iReaders = null;
	iVersion = 0;
	iID 	 = 0;
	iValue   = null;
	iLocker  = null;
    }

    public synchronized void	setValue(Object av)
    {
	if (iLocker !=null)
	{
	    //if someone has locked the data then wait
	    try{ wait() ; } 
	    catch(Exception e){}
	}

	//we don't need to lock here.
	iVersion ++;
	iValue = av;
	notifyReaders();
    }

    public synchronized Object	getValue()
    {
	if (iLocker!=null)	//someone is updating the data currently
	{
	    //if someone has locked the data then wait
	    try{ wait() ; } 
	    catch(Exception e){}
	}
	return iValue; 
    }
   
   /** use this mechanism if the pointed to data (iValue) is being changed but the
     *  pointer is remaining the same
     */
    public synchronized void	lock(Object l)
    {
	if (iLocker != null)
	{
	    //someone else has locked the data
	    try{ wait(); } 
	    catch(Exception e) {}
	}

	//now we lock the data
	iLocker = l;
    }

    public synchronized void	releaseAndNotify(Object l)
    {
	if (iLocker==l)	//if we locked the data
	{
	    iLocker=null;
	    iVersion ++;
	    notifyReaders();   
	    notifyAll();	//free any lockers who're waiting
	}
    }


    public	long	getID()
    {   return iID; }

    public	void	setID( long l )
    { iID = l; }

    private	void	notifyReaders()
    {
	//call the subscribers back
	if (iReaders ==null)	return;
	ListIterator	i = iReaders.listIterator(0);
	for(;i.hasNext();)
	{
	    Subscriber	s= (Subscriber) i.next();

	    s.notify( iID, iValue, iVersion );
	}
    }

    public synchronized	long	subscribe( Subscriber s )
    {
	if (iReaders == null)
	    iReaders = new LinkedList<Subscriber>();

	iReaders.add(s);
	return iID;
    }

    public synchronized	void	unSubscribe( Subscriber s )
    {
	if (iReaders !=null)
	{
	    iReaders.remove(s);
	}
    }
}

